package com.qubuyer.core.app

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import android.widget.ProgressBar
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.MutableLiveData
import com.google.android.material.snackbar.Snackbar
import com.hjq.toast.ToastUtils
import com.permissionx.guolindev.PermissionX
import com.qubuyer.core.R
import com.qubuyer.core.ext.dp
import com.qubuyer.core.utils.PermissionUtil
import com.qubuyer.core.utils.StatusBarUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import org.greenrobot.eventbus.EventBus
import kotlin.coroutines.CoroutineContext

/**
 * Author: linwg
 * Time: 2021/2/2 0002
 * Description:
 */
abstract class BaseActivity : AppCompatActivity(), IBaseView, CoroutineScope {

    override val coroutineContext: CoroutineContext
        get() = Job() + Dispatchers.Main
    lateinit var mContext: BaseActivity
    private var isLoadingDialogAlive = false
    private var loadingDialogShowStartTime: Long = -1L
    private var hasAddContentFragment = false
    var showingFragment: BaseFragment? = null
    lateinit var originFragment: BaseFragment
    private var containerId: Int = 0
    protected abstract val layoutId: Int
    private val progressDialog: Lazy<LoadingDialog> = lazy {
        LoadingDialog(this)
    }
    private val globalLoadingView: Lazy<View> = lazy {
        customLoadingView()
    }
    val fragmentTitle = MutableLiveData<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        mContext = this
        super.onCreate(savedInstanceState)
        setContentView(layoutId)
        StatusBarUtil.transparentStatusBar(this)
        if (savedInstanceState != null) {
            showingFragment = supportFragmentManager.getFragment(savedInstanceState, "showingFragment") as BaseFragment?
            fragmentTitle.value = showingFragment?.getTitle()
            val fragment = supportFragmentManager.getFragment(savedInstanceState, "originFragment")
            if (fragment != null) {
                originFragment = fragment as BaseFragment
            }
            containerId = savedInstanceState.getInt("containerId")
            hasAddContentFragment = savedInstanceState.getBoolean("hasAddContentFragment")
        }
        try {
            EventBus.getDefault().register(this)
        } catch (e: Exception) {

        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        if (this::originFragment.isInitialized) {
            supportFragmentManager.putFragment(outState, "originFragment", originFragment)
        }
        if (showingFragment != null) {
            supportFragmentManager.putFragment(outState, "showingFragment", showingFragment!!)
            outState.putBoolean("hasAddContentFragment", hasAddContentFragment)
            outState.putInt("containerId", containerId)
        }
        super.onSaveInstanceState(outState)
    }

    fun addContentFragment(fragment: BaseFragment, @IdRes id: Int) {
        if (hasAddContentFragment) {
            return
        }
        hasAddContentFragment = true
        containerId = id
        showingFragment = fragment
        originFragment = fragment
        supportFragmentManager.beginTransaction().also {
            if (fragment.isAdded) {
                it.show(fragment)
            } else {
                it.add(id, fragment)
            }
        }.addToBackStack(fragment.javaClass.simpleName).commit()
        fragmentTitle.value = showingFragment?.getTitle()
        addFragmentLifecycleCallbacks()
    }

    fun addFragmentLifecycleCallbacks() {
        supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
            override fun onFragmentDetached(fm: FragmentManager, f: Fragment) {
                if (f == showingFragment) {
                    showingFragment = showingFragment?.preFragment
                    fragmentTitle.value = showingFragment?.getTitle()
                    makeSureAdd()
                }
            }
        }, false)
    }

    /**
     * 确保该片段确实处于栈中（有可能被提前popBack掉了）
     */
    private fun makeSureAdd(){
        val fragments = supportFragmentManager.fragments
        while (showingFragment != null && !fragments.contains(showingFragment)){
            showingFragment = showingFragment?.preFragment
            fragmentTitle.value = showingFragment?.getTitle()
        }
    }

    fun swapContentFragment(fragment: BaseFragment) {
        fragment.preFragment = showingFragment
        supportFragmentManager.beginTransaction()
                .setCustomAnimations(R.anim.slide_right_in, R.anim.slide_left_out, R.anim.slide_left_in, R.anim.slide_right_out)
                .hide(showingFragment!!)
                .apply {
                    if (fragment.isAdded) {
                        this.show(fragment)
                    } else {
                        this.add(containerId, fragment)
                    }
                }
                .addToBackStack(fragment.javaClass.simpleName)
                .commitAllowingStateLoss()
        showingFragment = fragment
        fragmentTitle.value = showingFragment?.getTitle()
    }

    inline fun <reified T : BaseFragment> popBackTo(clazz: Class<T> = T::class.java) {
        supportFragmentManager.popBackStack(clazz.simpleName, 0)
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel()
        EventBus.getDefault().unregister(this)
    }

    override fun toast(msg: CharSequence) {
        ToastUtils.show(msg)
    }

    override fun toast(msgRes: Int) {
        ToastUtils.show(msgRes)
    }

    /**
     * 每个Activity只有一个加载对话框，试图弹出时，延迟250毫秒，
     * 如果这个过程中关闭加载对话框被调用了，那么此弹窗不会被实
     * 例化且显示
     */
    override fun showProgressDialog(hint: String?) {
        if (progressDialog.isInitialized()) {
            progressDialog.value.setMessage(hint)
        }
        window.decorView.postDelayed({
            if (isLoadingDialogAlive) {
                loadingDialogShowStartTime = System.currentTimeMillis()
                progressDialog.value.show()
                progressDialog.value.setMessage(hint)
            }
        }, 250)
        isLoadingDialogAlive = true
    }

    /**
     * 每个Activity只有一个加载对话框，弹出后试图关闭此弹窗，延迟最多500毫秒执行，
     * 如果这个过程中弹出加载对话框被调用了，那么此弹窗不会被关闭
     */
    override fun hideProgressDialog() {
        if (!isLoadingDialogAlive) {
            return
        }
        val l = System.currentTimeMillis() - loadingDialogShowStartTime
        val delay = if (l > 500 || loadingDialogShowStartTime == -1L) 0L else (500 - l)
        window.decorView.postDelayed({
            if (isLoadingDialogAlive) {
                return@postDelayed
            }
            if (!progressDialog.isInitialized()) {
                return@postDelayed
            }
            if (progressDialog.value.isShowing) {
                progressDialog.value.dismiss()
                isLoadingDialogAlive = false
            }
        }, delay)
        isLoadingDialogAlive = false
    }

    open fun customLoadingView(): View {
        return FrameLayout(mContext).apply {
            setBackgroundColor(Color.WHITE)
            isClickable = true
            addView(ProgressBar(mContext).apply { layoutParams = FrameLayout.LayoutParams(36.dp, 36.dp, Gravity.CENTER) })
        }
    }

    /**
     * 添加一个覆盖所有内容的加载视图，通常在整个页面是空的时候使用
     */
    override fun showPageLoadingView() {
        (window.decorView as ViewGroup).addView(globalLoadingView.value)
    }

    override fun hidePageLoadingView() {
        (window.decorView as ViewGroup).removeView(globalLoadingView.value)
    }

    override fun processNetworkInvalid() {
        Snackbar.make(window.decorView.findViewById(android.R.id.content), R.string.custom_toast_network_disconnected, Snackbar.LENGTH_SHORT).show()
    }

    override fun processTokenInvalid() {
        toast("登录失效，请重新登录")
    }

    private var onPermissionGranted: (() -> Unit)? = null
    private var deniedList: List<String>? = null

    fun checkPermissions(permissions: List<String>, onPermissionGranted: () -> Unit) {
        PermissionX.init(this).permissions(permissions)
                .request { allGranted, _, deniedList ->
                    if (allGranted) {
                        onPermissionGranted.invoke()
                    } else {
                        this.onPermissionGranted = onPermissionGranted
                        val permanentlyDeniedHit = when {
                            deniedList.contains(Manifest.permission.ACCESS_FINE_LOCATION) -> {
                                Pair(R.string.per_rationale_location_not_ask_again, R.string.per_rationale_location)
                            }
                            deniedList.contains(Manifest.permission.READ_PHONE_STATE) -> {
                                Pair(R.string.per_rationale_phone_not_ask_again, R.string.per_rationale_phone)
                            }
                            deniedList.contains(Manifest.permission.READ_EXTERNAL_STORAGE) -> {
                                Pair(R.string.per_rationale_read_external_storage_not_ask_again, R.string.per_rationale_read_external_storage)
                            }
                            deniedList.contains(Manifest.permission.CAMERA) -> {
                                Pair(R.string.per_rationale_camera_not_ask_again, R.string.per_rationale_camera)
                            }
                            else -> {
                                null
                            }
                        }
                        permanentlyDeniedHit?.let {
                            if (PermissionUtil.somePermissionPermanentlyDenied(this, deniedList)) {
                                showPermissionPermanentlyDeniedDialog(it.first, deniedList)
                            } else {
                                showPermissionDeniedDialog(it.second, deniedList, onPermissionGranted)
                            }
                        }
                    }
                }
    }

    private fun showPermissionDeniedDialog(second: Int, deniedList: List<String>, onPermissionGranted: () -> Unit) {

    }

    private fun showPermissionPermanentlyDeniedDialog(first: Int, deniedList: List<String>) {

    }

    /***
     * 收起软键盘
     */
    fun hideKeyBord() {
        val view = window.peekDecorView()
        if (view != null) {
            val imm =
                    getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(view.windowToken, 0)
        }
    }

    /***
     * 打开软键盘
     */
    fun showKeyBord() {
        val view = window.peekDecorView()
        if (view != null) {
            val imm =
                    getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.toggleSoftInput(
                    InputMethodManager.SHOW_IMPLICIT,
                    InputMethodManager.HIDE_NOT_ALWAYS
            )
        }
    }

    inline fun <reified T : Activity> startActivity(clazz: Class<T> = T::class.java) {
        startActivity(Intent(this, clazz))
    }


    inline fun <reified T : Activity> startActivity(clazz: Class<T> = T::class.java, block: ((Intent) -> Unit)) {
        val intent = Intent(this, clazz)
        block.invoke(intent)
        startActivity(intent)
    }

    inline fun <reified T : Activity> startActivityAndFinish(clazz: Class<T> = T::class.java, block: ((Intent) -> Unit)) {
        val intent = Intent(this, clazz)
        block.invoke(intent)
        startActivity(intent)
        finish()
    }

    inline fun <reified T : Activity> startActivityAndFinish(clazz: Class<T> = T::class.java) {
        val intent = Intent(this, clazz)
        startActivity(intent)
        finish()
    }
}